<template>
    <!-- 这里的v-bind和v-on是用来将props和事件透传给element-plus的table组件，v-on会校验，要特殊处理；因为props中定义了数据，所以$attrs没用了 -->
    <el-table class="config-table" v-bind="props" v-on="listenObj" ref="tableRef" v-loading="loading" >
        <el-table-column v-if="selectable" type="selection" :reserve-selection="false" 
            :selectableCall="selectableCall" :width="selectColumnWidth" />
        <slot name="operate_prefix">
            <!-- 头部操作插槽 -->
        </slot>
        <el-table-column v-bind="item" v-for="(item, index) in handledColumns" :key="item.prop" >
            <template #filter-icon="{ filterOpened }">
                <el-icon :class="{ 'filter-icon': true, 'active': filterOpened }"><Filter /></el-icon>
            </template>
        </el-table-column>
        <slot name="operate_suffix">
            <!-- 尾部操作插槽 -->
        </slot>
    </el-table>
</template>

<script lang="ts" setup>
/**
 * element-plus 可配置表格 组件说明：
 * 1. 排序： 
 * 前端排序，列设置为sortable=true和sort-method方法，则在点击列头时，会根据当前排序顺序进行排序，
 * 如果需要后端排序，需将 sortable 设置为 custom，同时在 Table 上监听 sort-change 事件（前端排序也会触发），
 * 默认 后端排序 
 * 在事件回调中可以获取当前排序的字段名和排序顺序，从而向接口请求排序后的表格数据。
 * 【注意】只能有一个字段排序，无法同时排序多个字段，多个排序混合又会有优先级的问题（可以搞一下专门的排序组件, 或者使用sort-by属性）。
 * 
 * 2. 过滤：
 * 使用后端过滤
 * 
 */
import { Filter, } from '@element-plus/icons-vue'
import { TableColumnCtx, TableInstance, TableProps } from "element-plus";
import { computed, ref } from "vue";
import { tableEvents, TableEventTypes } from './index';

// 禁用属性透传
defineOptions({
    inheritAttrs: false
});

// 列配置，合并element-plus的表格的column，因为必填项太多，所以直接全部改为可选项
type ColumnConfig = {
    label: string;
    prop: string;
} & Partial<TableColumnCtx>;


// 合并element-plus的表格的props
 export type ConfigTableProps = {
    columns: ColumnConfig[];
    data: any[];
    defaultColumnWidth?: string | number;     // 列默认宽度，也是最小宽度
    selectable?: boolean;                   // 是否可多选数据
    selectColumnWidth?: string | number;      // 勾选列宽度
    selectableCall?: (row: Record<string, any>, index: number) => boolean;   // 数据记录是否可以勾选

    loading?: boolean; // 是否加载中
    sortType?: 'backend'|'frontend'; // 数据排序方式，默认后端排序
} & TableProps<Record<PropertyKey, any>>;

const props = withDefaults(defineProps<ConfigTableProps>(), {
    columns: () => [],
    data: () => [],
    height: 'auto',

    // 覆盖默认配置, defineProps会将boolean默认值推导为false，所以封装链路组件都要复制一下这些默认值。
    size: 'large',
    fit: true,
    border: true,
    emptyText: '暂无数据',
    showHeader: true,
    defaultColumnWidth: 150,
    scrollbarAlwaysOn: true,

    // 多选配置
    rowKey: 'id',
    selectable: true,
    selectColumnWidth: 50,
    selectableCall: () => true,

    // 加载中
    loading: false,
    sortType: 'backend',
});

const handledColumns = computed(() => {
    const columnsList = props.columns.map((item, index) => {
        return Object.assign({}, item, {
            minWidth: props.defaultColumnWidth,
            width: getColumnWidth(item, index == props.columns.length - 1),
            sortable: props.sortType === 'backend' &&item.sortable === true ? 'custom' : item.sortable,
        });
    });
    return columnsList;
});


// defineEmits/defineProps 是编译时宏函数而非运行时函数，所以不能用变量，因为会报错，所以直接写死
const emits = defineEmits([
    'selection-change',
    'cell-click',
    'cell-dblclick',
    'row-click',
    'row-contextmenu',
    'row-dblclick',
    'header-click',
    'header-contextmenu',
    'sort-change',
    'filter-change',
    'current-change',
    'header-dragend',
    'expand-change',
]);

// 把table的事件冒泡出去
const emitEvent = (event: TableEventTypes, ...args: any[]) => {
    if(event === 'sort-change'&&props.sortType === 'frontend'){
        // 前端排序，自己处理排序，所以这里不抛出事件，直接返回
        return;
    }
    emits(event, ...args);
}
// 监听事件对象，直接原样抛出
const listenObj: any = tableEvents.reduce((obj: Record<string, any>, key: TableEventTypes) => {
    obj[key] = (...args: any[]) => emitEvent(key, ...args);
    return obj;
}, {});

// 表格实例
const tableRef = ref<TableInstance | null>(null);

// 获取列宽度
const getColumnWidth = (column: ColumnConfig, isLastColumn: boolean) => {

    if (column.width) {
        return column.width;
    }

    // 最后一列宽度自动，以完全占据剩余空间
    if (isLastColumn) {
        return column.width = 'auto';
    }

    return props.defaultColumnWidth;
}


defineExpose({
    clearSelection: ()=> tableRef.value?.clearSelection(),
    getSelectionRows: ()=> tableRef.value?.getSelectionRows(),
    toggleRowSelection: (row: any, selected?: boolean, ignoreSelectable = true)=> tableRef.value?.toggleRowSelection(row, selected, ignoreSelectable),
    toggleAllSelection: ()=> tableRef.value?.toggleAllSelection(),
    toggleRowExpansion: (row: any, expanded?: boolean)=> tableRef.value?.toggleRowExpansion(row, expanded),
    setCurrentRow: (row: any)=> tableRef.value?.setCurrentRow(row),
    clearSort: ()=> tableRef.value?.clearSort(),
    clearFilter: (columnKeys?: string[])=> tableRef.value?.clearFilter(columnKeys),
    doLayout: ()=> tableRef.value?.doLayout(),
    sort: (prop: string, order: string)=> tableRef.value?.sort(prop, order),
    getTable: ()=> tableRef.value,
    getColumns: ()=> tableRef.value?.columns,
});
</script>

<style lang="scss" scoped>
.config-table {
    width: 100%;
    $scrollBarSize: 8px;

    :deep(.el-scrollbar__bar.is-horizontal) {
        height: $scrollBarSize;
    }

    :deep(.el-scrollbar__bar.is-vertical) {
        width: $scrollBarSize;
    }

    .filter-icon{
        margin-left: 1em;
        
        &.active{
            color: var(--el-color-primary);
        }
    }
    :deep(.el-table__column-filter-trigger .el-icon){
        color: inherit;
    }

    
}


</style>